﻿using System;
using System.IO;
using System.Text;
using System.Collections.Generic;

namespace gKodes.IO
{
    public class DataStream : IDataSource
    {
        private byte[] bBuffer = new byte[512];

        private Stream mStream;

        private long mLlOffset;

        private Collections.Range mRange;

        private Dictionary<long, byte> mChanges = new Dictionary<long,byte>();

        public DataStream(Stream stream)
        {
            this.mStream = stream;
            this.mRange = new Collections.Range(0, stream.Length);
        }

        public long Length
        {
            get { return this.mStream.Length; }
        }

        public Collections.Range Range
        {
            get { return this.mRange; }
        }

        public byte this[int offset]
        {
            //get { return this.bBuffer[offset]; }
            get { return (mChanges.ContainsKey(mLlOffset + offset) ? this.mChanges[mLlOffset + offset] : this.bBuffer[offset]); }
        }

        //public Collections.Range Load(Collections.Range range)//IDataSource
        //{
        //    if (range.Length > this.bBuffer.Length)
        //        Array.Resize<byte>(ref this.bBuffer, (int)range.Length + 1);
            
        //    Collections.Range irange = Collections.Range.Intersection(this.mRange, range);
        //    this.mStream.Seek(range.Start, SeekOrigin.Begin);
        //    this.mStream.Read(this.bBuffer, 0, (int)irange.Length + 1);
        //    return irange;
        //}

        public Collections.Range Load(long offset, int length)
        {
            if (length > this.bBuffer.Length)
                Array.Resize<byte>(ref this.bBuffer, length);

            mLlOffset = offset; // TODO check the range length before assigining any values
            Collections.Range irange = new Collections.Range(offset, offset);
            this.mStream.Seek(offset, SeekOrigin.Begin);
            irange.Length = this.mStream.Read(this.bBuffer, 0, length) - 1;
            //mChanges.
            return irange;
        }

        public int Read(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }

        public void Write(long offset, short nibble, byte value)
        {
            if (nibble < 0 || nibble > 2)
                throw new ArgumentOutOfRangeException("nibble");

            System.Diagnostics.Debug.WriteLine(value + "@ " + offset);
            if (nibble > 0)
            {
                byte oldValue = 0;
                if (this.mChanges.ContainsKey(offset))
                    oldValue = this.mChanges[offset];
                else
                {
                    this.mStream.Seek(offset, SeekOrigin.Begin);
                    oldValue = (byte)this.mStream.ReadByte();
                }
                value = ReplaceNibble(nibble - 1, oldValue, (byte)(value & 0x0F));
            }
            this.mChanges[offset] = value;
        }

        public void Dispose()
        {
            this.mStream.Dispose();
        }

        internal static byte ReplaceNibble(int place, byte value, byte nibble)
        {
            return ((place == 1) ? (byte)((value & 0xF0) | nibble) : (byte)((value & 0x0F) | (nibble << 4)) );
        }
    }
}